﻿using EWS;
using Microsoft.Exchange.WebServices.Data;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using XF.Common;
using XF.Meeting.Entity.Query;

namespace DYUserCenterSync
{
    partial class EWSservice : ServiceBase
    {
        public EWSservice()
        {
            InitializeComponent();
        }     
        protected override void OnStart(string[] args)
        {
            // TODO: 在此处添加代码以启动服务。
            _connections = new Dictionary<string, StreamingSubscriptionConnection>();
            CreateGroup();
            foreach (string sGroup in _groups.Keys)
            {
          
                AddGroupSubscriptions(sGroup);
            }
            foreach (StreamingSubscriptionConnection connection in _connections.Values)
            {
          
                connection.Open();
            }
            CLogger.Info("--------- StreamSubscription event -------");
            System.Timers.Timer timer = new System.Timers.Timer(10000);
            timer.Elapsed += ReconnectToSubscriptions;
            timer.AutoReset = true;
            timer.Enabled = true;
            timer.Start();
        }

        protected override void OnStop()
        {
            // TODO: 在此处添加代码以执行停止服务所需的关闭操作。
            foreach (var item in _connections)
            {
                item.Value.Close();
            }
        }
        static Dictionary<string, GroupInfo> _groups = new Dictionary<string, GroupInfo>();

        public static Mailboxes _mailboxes { get; set; }
        public static Dictionary<string, StreamingSubscription> _subscriptions { get; private set; }
        public static bool _reconnect { get; set; }
        public static object _reconnectLock { get; set; } = new object();
        public static ExchangeToDbService apponit = new ExchangeToDbService("", "");

        private static Dictionary<string, StreamingSubscriptionConnection> _connections = null;
        private static readonly string EWSAccount = ConfigHelper.GetConfg("EWSAccount");
        private static readonly string EWSAccountPwd = ConfigHelper.GetConfg("EWSAccountPwd");
        private static void AddGroupSubscriptions(string sGroup)
        {
            if (!_groups.ContainsKey(sGroup))
                return;

            if (_connections.ContainsKey(sGroup))
            {
                foreach (StreamingSubscription subscription in _connections[sGroup].CurrentSubscriptions)
                {
                    try
                    {
                        subscription.Unsubscribe();
                    }
                    catch { }
                }
                try
                {
                    _connections[sGroup].Close();
                }
                catch { }
            }

            try
            {
                // Create the connection for this group, and the primary mailbox subscription
                GroupInfo group = _groups[sGroup];
                StreamingSubscription subscription = AddSubscription(group.PrimaryMailbox, group);

                if (_connections.ContainsKey(sGroup))
                {
                    _connections[sGroup] = new StreamingSubscriptionConnection(subscription.Service, 30);
                }
                else
                    _connections.Add(sGroup, new StreamingSubscriptionConnection(subscription.Service, 30));

                SubscribeConnectionEvents(_connections[sGroup]);
                _connections[sGroup].AddSubscription(subscription);

                // Now add any further subscriptions in this group
                foreach (string sMailbox in group.Mailboxes)
                {
                    if (!sMailbox.Equals(group.PrimaryMailbox))
                    {
                        try
                        {
                            subscription = AddSubscription(sMailbox, group);
                            _connections[sGroup].AddSubscription(subscription);

                        }
                        catch (Exception ex)
                        {

                        }
                    }
                }
            }
            catch (Exception ex)
            {

            }

        }

        private static void SubscribeConnectionEvents(StreamingSubscriptionConnection connection)
        {
            connection.OnNotificationEvent += OnEvent;
            connection.OnDisconnect += OnDisconnect;
            connection.OnSubscriptionError += OnError;
        }
        private static StreamingSubscription AddSubscription(string sMailbox, GroupInfo group)
        {
            if (_subscriptions == null)
                _subscriptions = new Dictionary<string, StreamingSubscription>();
            if (_subscriptions.ContainsKey(sMailbox))
                _subscriptions.Remove(sMailbox);
            ExchangeService exchange = group.ExchangeService;
            exchange.Credentials = new WebCredentials(EWSAccount, EWSAccountPwd);
            exchange.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, sMailbox);
            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
            var subscription = exchange.SubscribeToStreamingNotifications(
                   new FolderId[] { WellKnownFolderName.Calendar, WellKnownFolderName.DeletedItems },
                   EventType.Created, EventType.Modified, EventType.Moved
                 );
            _subscriptions.Add(sMailbox, subscription);
            return subscription;

        }

        private static void ReconnectToSubscriptions(object sender, EventArgs e)
        {
            // Go through our connections and reconnect any that have closed
            _reconnect = false;
            lock (_reconnectLock)  // Prevent this code being run concurrently (i.e. if an event fires in the middle of the processing)
            {

                foreach (string sConnectionGroup in _connections.Keys)
                {
                    StreamingSubscriptionConnection connection = _connections[sConnectionGroup];

                    if (!connection.IsOpen)
                    {
                        try
                        {
                            try
                            {
                                connection.Close();
                                connection.Open();
                                CLogger.Info(String.Format("重新连接组 {0}", sConnectionGroup));
                            }
                            catch (Exception ex)
                            {
                                if (ex.Message.StartsWith("订阅打开之前必须添加订阅"))
                                {
                                    // Try recreating this group
                                    AddGroupSubscriptions(sConnectionGroup);
                                }
                                else
                                    CLogger.Info(String.Format("打开订阅 {0}失败", ex.Message));

                            }
                        }
                        catch (Exception ex)
                        {
                            CLogger.Info(String.Format("重新发起订阅失败 {0}", ex.Message));
                        }
                    }
                }
            }
        }

        private static void CreateGroup()
        {
            var service = ExChangeHelper.getExChangeHelper();
            //  ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
            var meetss = service.GetRoomLists();
            _groups = new Dictionary<string, GroupInfo>();
            _mailboxes = new Mailboxes(new WebCredentials(EWSAccount, EWSAccountPwd));
            foreach (var meets in meetss)
            {
                var meetList = service.GetRooms(meets);
                foreach (var meet in meetList)
                {
                    _mailboxes.AddMailbox(meet.Address);
                    MailboxInfo mailboxInfo = _mailboxes.Mailbox(meet.Address);
                    if (mailboxInfo != null)
                    {
                        GroupInfo groupInfo = null;
                        if (_groups.ContainsKey(mailboxInfo.GroupName))
                        {
                            groupInfo = _groups[mailboxInfo.GroupName];
                        }
                        else
                        {
                            groupInfo = new GroupInfo(mailboxInfo.GroupName, mailboxInfo.SMTPAddress, mailboxInfo.EwsUrl);
                            _groups.Add(mailboxInfo.GroupName, groupInfo);
                        }
                        if (groupInfo.Mailboxes.Count > 199)
                        {
                            // We already have enough mailboxes in this group, so we rename it and create a new one
                            // Renaming it means that we can still put new mailboxes into the correct group based on GroupingInformation
                            int i = 1;
                            while (_groups.ContainsKey(String.Format("{0}{1}", groupInfo.Name, i)))
                                i++;
                            _groups.Remove(groupInfo.Name);
                            _groups.Add(String.Format("{0}{1}", groupInfo.Name, i), groupInfo);
                            groupInfo = new GroupInfo(mailboxInfo.GroupName, mailboxInfo.SMTPAddress, mailboxInfo.EwsUrl);
                            _groups.Add(mailboxInfo.GroupName, groupInfo);
                        }

                        groupInfo.Mailboxes.Add(meet.Address);
                    }
                }
            }
        }


        private static void OnDisconnect(object sender, SubscriptionErrorEventArgs args)
        {
            // Cast the sender as a StreamingSubscriptionConnection object.           


            CLogger.Info("订阅失效。");

            _reconnect = true;



        }
        #region 监听会议流转
        /*Calendar ：      新建会议： craeted 一条Appointment会议信息==> FreeBusyChanged=>Modified=>FreeBusyChanged 事件中各会有一条Appointment会议信息 。
         *                        修改会议： Modified =>FreeBusyChanged=>Modified=>FreeBusyChanged 一条Appointment会议信息
         *                        取消会议：暂未发现
         *                        会议冲突：不显示。
          
           Send Items：    新建会议： Created 事件  会有一条Item信息， 具体回复是已接受：xxxxx   
                                   修改会议： Created 一条Item邮件    回复 已接受：xxxxxxxx
                                   取消会议： 暂未发现
                                   会议冲突：  Created 一条 Item     subject:已拒绝：xxxxxxxxxxxxx
         
           Deleted Items   修改会议：  Moved 一条Item邮件。
                                   取消会议：  Moved=>FreeBusyChanged 一条Appointment会议信息，subject：已取消：xxxxx
                                                     Moved 一条Item邮件。 subject=已取消：xxxxxxxxx
                                   会议冲突：  Moved=>FreeBusyChanged 一条Appointment会议信息，subject：xxxxx
                                                     Moved 一条Item。subject=xxxx
                                                    Created 一条 Item


        新建会议：  Calendar craeted 一条Appointment会议信息
        修改会议:     Calendar Modified  一条Appointment会议信息
        取消会议：   Deleted Items Moved  一条Appointment会议信息 subject：已取消：xxxxx
        会议冲突：   Deleted Items Moved 一条Appointment会议信息，subject：xxxxx
         */
        #endregion
        static void OnEvent(object sender, NotificationEventArgs args)
        {
            CLogger.Info("===========================================");
            var status = 0;
            // Loop through all item-related events. 
            foreach (NotificationEvent notification in args.Events)
            {
                if (notification is ItemEvent)
                {
                    StreamingSubscription subscription = args.Subscription;
                    NotificationInfo info;
                    info.Mailbox = subscription.Service.ImpersonatedUserId.Id;
                    info.Event = notification;
                    info.Service = subscription.Service;
                    var item = (ItemEvent)info.Event;
                    var folder = Folder.Bind(info.Service, item.ParentFolderId, new PropertySet(FolderSchema.DisplayName));
                    #region 监听事件
                    /*
                     会议冲突--监听已发送邮件中包含已拒绝的邮件。 
                     会议预约--监听日历的created事件。并且是 Appointment 会议类型
                    会议修改---监听日历的Modify事件 并且 Appointment 会议类型
                    会议取消--监听已删除邮件 中 FreeBusyChanged事件。并且Appointment 会议类型
                     */
                    if (folder.DisplayName.Equals(WellKnownFolderName.Calendar.ToString()) || folder.DisplayName.Equals("日历"))
                    {
                        if (info.Event.EventType.Equals(EventType.Created))
                        {
                            status = 1;
                            try
                            {
                                var AAA = Item.Bind(info.Service, item.ItemId, new PropertySet(BasePropertySet.IdOnly));
                                if (AAA is Appointment)
                                {
                                    Appointment oAppt = Appointment.Bind(info.Service, AAA.Id);
                                    if (oAppt.AppointmentType == AppointmentType.RecurringMaster || oAppt.AppointmentType == AppointmentType.Occurrence)
                                    {
                                        CLogger.Info($"新建周期会议{oAppt.Subject}开始时间:{oAppt.Start}-{oAppt.End},会议室：{oAppt.Location},发件人：{oAppt.Organizer.Address},ICalUid:{ oAppt.ICalUid } ");
                                        apponit.insertZhouqi(oAppt);

                                    }
                                    else
                                    {
                                        CLogger.Info($"新建会议{oAppt.Subject}开始时间:{oAppt.Start}-{oAppt.End},会议室：{oAppt.Location},发件人：{oAppt.Organizer.Address},ICalUid:{ oAppt.ICalUid } ");
                                        apponit.insert(new ExchangeQuery(oAppt.ICalUid, oAppt.Organizer.Address, oAppt.Start, oAppt.End, oAppt.Id.ToString()), info.Mailbox);
                                    }
                                    continue;
                                }
                            }
                            catch (Exception e)
                            {
                                continue;

                            }


                        }
                        else if (info.Event.EventType.Equals(EventType.Modified) && status == 0)
                        {

                            try
                            {

                                var AAA = Item.Bind(info.Service, item.ItemId, new PropertySet(BasePropertySet.IdOnly));
                                if (AAA is Appointment)
                                {
                                    Appointment oAppt = Appointment.Bind(info.Service, AAA.Id);
                                    if (oAppt.AppointmentType == AppointmentType.RecurringMaster || oAppt.AppointmentType == AppointmentType.Occurrence)
                                    {
                                        CLogger.Info($"更新周期会议{oAppt.Subject}开始时间:{oAppt.Start}-{oAppt.End},会议室：{oAppt.Location},发件人：{oAppt.Organizer.Address},ICalUid:{ oAppt.ICalUid } ");
                                        apponit.updateZhouqi(oAppt);
                                        continue;
                                    }
                                    else
                                    {
                                        CLogger.Info($"更新会议{oAppt.Subject}开始时间:{oAppt.Start}-{oAppt.End},会议室：{oAppt.Location},发件人：{oAppt.Organizer.Address},ICalUid:{ oAppt.ICalUid } ");
                                        var result = apponit.Update(new ExchangeQuery(oAppt.ICalUid, oAppt.Organizer.Address, oAppt.Start, oAppt.End, oAppt.Id.ToString()));
                                        if (!result.Result)
                                        {
                                            continue;
                                        }
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                continue;

                            }
                        }

                    }
                    else if (folder.DisplayName.Equals(WellKnownFolderName.DeletedItems.ToString()) || folder.DisplayName.Equals("Deleted Items") || folder.DisplayName.Equals("已删除邮件"))
                    {
                        if (info.Event.EventType.Equals(EventType.Moved))
                        {
                            try
                            {
                                var AAA = Item.Bind(info.Service, item.ItemId, new PropertySet(BasePropertySet.IdOnly));
                                if (AAA is Appointment)
                                {
                                    Appointment oAppt = Appointment.Bind(info.Service, AAA.Id);
                                    if (oAppt.Subject.Contains("已取消"))
                                    {
                                        CLogger.Info($"取消会议{oAppt.Subject}开始时间:{oAppt.Start}-{oAppt.End},会议室：{oAppt.Location},发件人：{oAppt.Organizer.Address},ICalUid:{ oAppt.ICalUid } ");
                                        var result = apponit.Cannel(oAppt);
                                        if (result)
                                        {
                                            break;
                                        }
                                        else
                                        {

                                            CLogger.Info($"{oAppt.Subject}取消失败");

                                        }
                                        break;
                                    }
                                    apponit.NotAccept(oAppt);



                                }
                            }
                            catch (Exception e)
                            {
                                continue;

                            }


                        }
                    }
                    #endregion 
                }
            }
        }



        static void OnError(object sender, SubscriptionErrorEventArgs args)
        {
            // Handle error conditions. 
            Exception e = args.Exception;
            CLogger.Info("\n-------------Error ---" + e.Message + "-------------");
        }
    }
    struct NotificationInfo
    {
        public string Mailbox;
        public NotificationEvent Event;
        public ExchangeService Service;
    }
}
